#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
'''
@File    :   server.py
@Time    :   2022/11/25 17:14:11
@Author  :   youle
@Email   :   a_fly0505@163.com
@Copyright : 侵权必究
'''

from __future__ import annotations
from technicalindicator.indicators import Indicators
from core.config import Config
from stock.stock_hist import StockHist
from stock.query_stock import StockQuery
import model.db_models as dbModel
import model.db_operator as dbOperator
from core.cache import Cache
import numpy as np
import pandas as pd
import akshare as ak
import datetime
import chinese_calendar
import core.utils as utils
import logging

class Filter:
    def __init__(self) -> None:
        self.logger = logging.getLogger('stock')
        pass

    def FStock(self, code: str) -> bool:
        self.logger.debug("base filter")
        return False

    def FFund(self, code: str) -> bool:
        self.logger.debug("base filter")
        return False


class Server():
    def __init__(self, config: Config, filter=Filter()) -> None:
        self.logger = logging.getLogger('stock')
        dbModel.Init(config)
        self._config = config
        self._stock_hist = StockHist(config)
        self._stock_query = StockQuery(config)
        self._indicators = Indicators(config)
        self._cache = Cache()
        self._filter = filter
        self._fund_today_sync = False

    def GetStockRSI(self, thres: float = 26.0, timeperiod: int = 14, last: int = 5) -> list[str]:
        stocks_array = self._stock_query.GetAllStock()
        stocks = []
        for item in stocks_array:
            code: str = item[0]
            sname = item[1]
            # 过滤掉科创板
            if code.startswith("68") or code.startswith("8"):
                continue
            is_ok = self.GetOneStockRSI(code, sname, thres=thres, timeperiod=timeperiod, last=last)
            if is_ok:
                stocks.append({"code":code, "name":sname})
        return stocks

    def GetOneStockRSI(self, code: str, sname: str, thres: float = 26, timeperiod: int = 14, last: int = 5) -> bool:
        df, exist = self._stock_query.GetStockData(code)
        if exist == False:
            return False
        if len(df.values) < self._config.recoderLimit:
            return False
        qual, rsiData = self._indicators.RSI(df, threshold=thres, timeperiod=timeperiod, last=last)
        # self._cache.SetRSI(code, rsiData)
        if qual:
            return True

    def GetOneStockKDJ(self, code: str, sname: str, fastk: int = 9, slowd: int = 3, slowj: int = 3) -> bool:
        df, exist = self._stock_query.GetStockData(code)
        if exist == False:
            return False
        if len(df.values) < self._config.recoderLimit:
            return False
        self._indicators.KDJ(df, fastk=fastk, slowd=slowd, slowj=slowj)

############## fund ####################
    # 获取etf的RSI
    def GetFundRSI(self, thres: int = 30.0, timeperiod: int = 14, last: int = 1) -> list[str]:
        '''
        '''
        filter_fund = []
        # 查询所有的基金代码
        funds = dbOperator.g_market_fund_op.QueryAll()
        for fund in funds:
            self.logger.debug("run rsi etfcode:{} sname:{}".format(fund.code, fund.sname))
            isOk = self.GetOneFundRSI(
                fund.code, fund.sname, thres=thres, last=last, timeperiod=timeperiod)
            if isOk:
                filter_fund.append({"code":fund.code, "name":fund.sname})
        self.logger.debug("Fund RSI last={}, timeperiod={}, threshold={}".format(
            last, timeperiod, thres))
        return filter_fund

    def GetOneFundRSI(self, code: str, sname: str, thres: int = 30.0, timeperiod: int = 14,  last: int = 1) -> bool:
        # 查询指定基金的所有数据
        df, ret = dbOperator.g_fund_op.QueryAll(code)
        if ret == False:
            return False
        if len(df.values) < self._config.recoderLimit:
            return False
        qual, rsiData = self._indicators.RSI(df, threshold=thres, timeperiod=timeperiod, last=last)
        if qual:
            return True
            # filter_fund.append(fund.code)

    def SyncFundEveryDay(self) -> None:
        self.logger.info("begin SyncFundEveryDay")
        now = datetime.datetime.now()
        # now = now - datetime.timedelta(hours=6)
        today = now.date()
        is_workday = chinese_calendar.is_workday(today)
        if not is_workday:
            self.logger.info("today={} is not workday, do nothing".format(today))
            return
        open_time = datetime.datetime(now.year, now.month, now.day, 9, 35, 0)
        close_time = datetime.datetime(now.year, now.month, now.day, 16, 0, 0)
        if now < open_time:
            self.logger.debug("now market haven't opened, please wait until opening")
            return
        elif now > open_time and now <= close_time:
            self.logger.debug("now market opening, update temporary")
            self._SyncFundEveryDay(today)
        else:
            if self._fund_today_sync == True:
                self.logger.debug("fund have sync today, do nothing")
                return
            # 今天开始做最后的同步
            self._fund_today_sync = True
            self._SyncFundEveryDay(today)

    def _SyncFundEveryDay(self, today:datetime.date) -> None:
        fund_etf_df = ak.fund_etf_spot_em()
        for item in fund_etf_df.values:
            code: str = item[0]
            sname: str = item[1]

            table_name = "fund_{}_{}".format("qfq", code)
            is_exist = dbOperator.g_fund_op.CheckTable(table_name, "fund")
            if not is_exist:
                self.logger.info("create fund:{}:{}".format(code, sname))
                dbOperator.g_fund_op.CreateAllTables([code])
                begin_date = today - datetime.timedelta(days=7)
                end_date = today + datetime.timedelta(days=3)
                self.logger.debug("get fund:{}:{} begin begin_date:{}, end_date{}".format(code, sname, begin_date, end_date))
                self.UpdateFund(code, beginDate=begin_date, endDate=end_date)
                return


            FundObject =  dbOperator.g_market_fund_op.GetFundTable(code)
            fund_obj = dbOperator.g_fund_op.QueryLastDay(FundObject)
            lastDate:datetime.date = fund_obj.date
            # 是否只隔了一个工作日
            isOneDay = utils.IsOneDay(today, lastDate)
            if not isOneDay:
                # 大于一个工作日，查询历史数据，如数据库
                # 历史数据开始的日期
                begin_date = lastDate+datetime.timedelta(days=1)
                end_date = today+datetime.timedelta(days=1)
                self.logger.debug("get fund:{}:{} begin begin_date:{}, end_date{}".format(code, sname, begin_date, end_date))
                self.UpdateFund(code, beginDate=begin_date, endDate=end_date)
            else:
                # "f12": "代码",  0
                # "f14": "名称",  1
                # "f2": "最新价", 2
                # "f4": "涨跌额", 3
                # "f3": "涨跌幅", 4
                # "f5": "成交量", 5
                # "f6": "成交额", 6
                # "f17": "开盘价",7
                # "f15": "最高价",8
                # "f16": "最低价",9
                # "f18": "昨收",  10
                # "f8": "换手率",  11
                # "f21": "流通市值", 12
                # "f20": "总市值", 13
                ##################################
                # date, open, close, hight, low, volume, volume_quota, vibration, rise_fall_rate, rise_fall_quota, turnover_rate)
                self.logger.debug("get fund:{}:{}".format(code, sname))
                now_close = item[2]  # close
                open = item[7]
                hight = item[8]
                low = item[9]
                volume = item[5]
                volume_quota = item[6]
                vibration = 0.0
                rise_fall_rate = item[4]
                rise_fall_quota = item[3]
                turnover_rate = item[11]
                fund_item = FundObject(date=today, open=open, close=now_close, hight=hight, low=low, volume=volume,
                                       volume_quota=volume_quota, vibration=vibration, rise_fall_rate=rise_fall_rate, rise_fall_quota=rise_fall_quota, turnover_rate=turnover_rate)
                dbOperator.g_fund_op.Merge(FundObject, fund_item)

    # 跟新单个基金的历史数据
    def UpdateFund(self, code: str, beginDate: datetime.date, endDate: datetime.date) -> bool:
        start_date = beginDate.strftime("%Y%m%d")
        end_date = endDate.strftime("%Y%m%d")
        fund_etf_hist_df = ak.fund_etf_hist_em(
            symbol=code, period="daily", start_date=start_date, end_date=end_date, adjust="")
        fund_datas = []
        FundObject = dbOperator.g_market_fund_op.GetFundTable(code)
        for item in fund_etf_hist_df.values:
            fundItem = FundObject(date=item[0], open=item[1], close=item[2], hight=item[3], low=item[4], volume=item[5],
                                  volume_quota=item[6], vibration=item[7], rise_fall_rate=item[8], rise_fall_quota=item[9], turnover_rate=item[10])
            fund_datas.append(fundItem)
        dbOperator.g_fund_op.InsertList(code, fund_datas)
